home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / fmodla13.zip / TALK.ZIP / RS232.MOD < prev    next >
Text File  |  1987-10-18  |  14KB  |  464 lines

  1. IMPLEMENTATION MODULE RS232;
  2.  
  3. (* (C) Copyright 1987 Fitted Software Tools. All rights reserved.
  4.  
  5.     This module is part of the example multitasking communications program
  6.     provided with the Fitted Software Tools' Modula-2 development system.
  7.  
  8.     Registered users may use this program as is, or they may modify it to
  9.     suit their needs or as an exercise.
  10.  
  11.     If you develop interesting derivatives of this program and would like
  12.     to share it with others, we encourage you to upload a copy to our BBS.
  13. *)
  14.  
  15. (* $L+ *)
  16. (* $S-, $R-, $T- *)
  17.  
  18. FROM SYSTEM     IMPORT ASSEMBLER, ADR, ADDRESS, SEG, OFS;
  19. FROM System     IMPORT TermProcedure, GetVector, ResetVector;
  20. FROM Storage    IMPORT ALLOCATE, DEALLOCATE, Available;
  21. FROM ASCII      IMPORT CtrlS, CtrlQ;
  22. FROM Kernel     IMPORT InitSignal, NewProcess, Signal, WaitIO;
  23.  
  24. CONST
  25.     Com1IntNo   = 12;               (* COM1 interrupt vector *)
  26.     Com2IntNo   = 11;               (* COM2 interrupt vector *)
  27.  
  28.     (* 8250 ports *)
  29.     RX          = 0;                (* xmit reg. when DLAB = 0 *)
  30.     TX          = 0;                (* rcv reg. when DLAB = 0 *)
  31.     DivL        = 0;                (* baud rate divisor when DLAB = 1 *)
  32.     DivH        = 1;                (*   "    "     "    when DLAB = 1 *)
  33.     IntEna      = 1;                (* interrupt enable *)
  34.     IntId       = 2;                (* interrupt id *)
  35.     LCR         = 3;                (* line status register *)
  36.     MCR         = 4;                (* modem control register *)
  37.     LSR         = 5;                (* line status register *)
  38.     MSR         = 6;                (* modem status register *)
  39.  
  40. TYPE BufferPointer = POINTER TO ARRAY [0..65531] OF CHAR;
  41.  
  42. VAR ComPort     :CARDINAL;          (* Com port in use *)
  43.     ComIntNo    :CARDINAL;          (* Int # for port in use *)
  44.     ComBase     :CARDINAL;          (* base io adrs for port in use *)
  45.     ComRX       :CARDINAL;          (* receive register *)
  46.     ComLSR      :CARDINAL;          (* line status register *)
  47.  
  48.     OldCom1Int  :ADDRESS;           (* save old ISR adrs here *)
  49.     OldCom2Int  :ADDRESS;
  50.     Com1Base    :CARDINAL;          (* base ports of 8250 ACA *)
  51.     Com2Base    :CARDINAL;
  52.  
  53.     ISR1active  :BOOLEAN;           (* ISR process created? *)
  54.     ISR2active  :BOOLEAN;
  55.  
  56.     (* Receive buffer *)
  57.     BuffPtr     :BufferPointer;
  58.     BuffSize    :CARDINAL;
  59.     InPtr       :CARDINAL;
  60.     OutPtr      :CARDINAL;
  61.     InCount     :CARDINAL;
  62.  
  63.     xon             :BOOLEAN;       (* use xon/off ? *)
  64.     XoffThreshold   :CARDINAL;
  65.     XonThreshold    :CARDINAL;
  66.     XoffSent        :BOOLEAN;
  67.  
  68.  
  69. MODULE RS232Receiver[3];
  70. (*
  71.     RS232Receiver runs at priority 3. The actual COM priority is
  72.     either 3 or 4, depending on the port in use.
  73. *)
  74.  
  75.     IMPORT ASSEMBLER, WaitIO, Signal, RS232Input,
  76.            ComIntNo, ComBase, LSR, RX, TX, MCR, LCR, IntEna,
  77.            BuffSize, BuffPtr, InCount, InPtr, xon, CtrlS,
  78.            XoffThreshold, XoffSent;
  79.  
  80.     EXPORT ComInt1, ComInt2, StartReading, StopReading;
  81.  
  82.     (*
  83.         Even though we have one ISR for each COM port, only one
  84.         of those ports may be active at any one time!
  85.     *)
  86.     PROCEDURE ComInt1;
  87.     (*
  88.         ISR for COM1
  89.     *)
  90.     BEGIN
  91.         StartReading;
  92.         LOOP
  93.             WaitIO( 12 );
  94.             ASM
  95.                 MOV     BX, ComBase         (* read line status reg *)
  96.                 LEA     DX, [BX+LSR]
  97.                 IN      AL, DX
  98.                 TEST    AL, 1               (* input character? *)
  99.                 JZ      exit
  100.                 LEA     DX, [BX+RX]         (* grab input character *)
  101.                 IN      AL, DX
  102.                 MOV     CX, InCount         (* does it fit in the buffer? *)
  103.                 CMP     CX, BuffSize
  104.                 JAE     exit
  105.                 LES     DI, BuffPtr         (* buffer with it! *)
  106.                 MOV     BX, InPtr
  107.                 MOV     ES:[DI+BX], AL
  108.                 INC     InCount             (* adjust counters *)
  109.                 INC     BX
  110.                 CMP     BX, BuffSize
  111.                 JB      ok
  112.                 XOR     BX, BX
  113.             ok: MOV     InPtr, BX
  114.                 TEST    xon, 1              (* xon/xoff enabled ? *)
  115.                 JZ      exit
  116.                 CMP     CX, XoffThreshold
  117.                 JB      exit
  118.                 TEST    XoffSent, 1
  119.                 JNZ     exit
  120.                 MOV     BX, ComBase         (* send XOFF is xmit reg empty *)
  121.                 LEA     DX, [BX+LSR]
  122.                 IN      AL, DX
  123.                 TEST    AL, 20H
  124.                 JZ      exit
  125.                 MOV     AL, CtrlS
  126.                 LEA     DX, [BX+TX]
  127.                 OUT     DX, AL
  128.                 MOV     XoffSent, 1
  129.  
  130.             exit:
  131.                 MOV     AL, 20H             (* send EOI *)
  132.                 OUT     20H, AL
  133.             END;
  134.             IF InCount > 0 THEN Signal( RS232Input ) END;
  135.         END;
  136.     END ComInt1;
  137.  
  138.  
  139.     PROCEDURE ComInt2;
  140.     (*
  141.         ISR for COM2
  142.     *)
  143.     BEGIN
  144.         StartReading;
  145.         LOOP
  146.             WaitIO( 11 );
  147.             ASM
  148.                 MOV     BX, ComBase         (* read line status reg *)
  149.                 LEA     DX, [BX+LSR]
  150.                 IN      AL, DX
  151.                 TEST    AL, 1               (* input character? *)
  152.                 JZ      exit
  153.                 LEA     DX, [BX+RX]         (* grab input character *)
  154.                 IN      AL, DX
  155.                 MOV     CX, InCount         (* does it fit in the buffer? *)
  156.                 CMP     CX, BuffSize
  157.                 JAE     exit
  158.                 LES     DI, BuffPtr         (* buffer with it! *)
  159.                 MOV     BX, InPtr
  160.                 MOV     ES:[DI+BX], AL
  161.                 INC     InCount             (* adjust counters *)
  162.                 INC     BX
  163.                 CMP     BX, BuffSize
  164.                 JB      ok
  165.                 XOR     BX, BX
  166.             ok: MOV     InPtr, BX
  167.                 TEST    xon, 1              (* xon/xoff enabled ? *)
  168.                 JZ      exit
  169.                 CMP     CX, XoffThreshold
  170.                 JB      exit
  171.                 TEST    XoffSent, 1
  172.                 JNZ     exit
  173.                 MOV     BX, ComBase         (* send XOFF is xmit reg empty *)
  174.                 LEA     DX, [BX+LSR]
  175.                 IN      AL, DX
  176.                 TEST    AL, 20H
  177.                 JZ      exit
  178.                 MOV     AL, CtrlS
  179.                 LEA     DX, [BX+TX]
  180.                 OUT     DX, AL
  181.                 MOV     XoffSent, 1
  182.  
  183.             exit:
  184.                 MOV     AL, 20H             (* send EOI *)
  185.                 OUT     20H, AL
  186.             END;
  187.             IF InCount > 0 THEN Signal( RS232Input ) END;
  188.         END;
  189.     END ComInt2;
  190.  
  191.  
  192.     VAR Reading :BOOLEAN;
  193.  
  194.     PROCEDURE StartReading;
  195.     BEGIN
  196.         ASM
  197.             MOV     BX, ComBase             (* clear all 8250 registers *)
  198.             LEA     DX, [BX+LSR]
  199.             IN      AL, DX
  200.             LEA     DX, [BX+RX]
  201.             IN      AL, DX
  202.             LEA     DX, [BX+MCR]
  203.             IN      AL, DX
  204.  
  205.             MOV     BX, ComBase
  206.             LEA     DX, [BX+IntEna]         (* enable receiver interrupts *)
  207.             MOV     AL, 5
  208.             OUT     DX, AL
  209.             JMP     slownow
  210.           slownow:
  211.  
  212.             LEA     DX, [BX+MCR]            (* modem control *)
  213.             MOV     AL, 0BH                 (* out2 + RTS + DTR *)
  214.             OUT     DX, AL
  215.             JMP     slowagain
  216.           slowagain:
  217.  
  218.             LEA     DX, [BX+RX]             (* read data reg just in case... *)
  219.             IN      AL, DX
  220.         END;
  221.         Reading := TRUE;
  222.     END StartReading;
  223.  
  224.  
  225.     PROCEDURE StopReading;
  226.     BEGIN
  227.         IF Reading THEN
  228.             ASM
  229.                 MOV     BX, ComBase         (* disable 8250 interrupts *)
  230.                 LEA     DX, [BX+IntEna]
  231.                 MOV     AL, 0
  232.                 OUT     DX, AL
  233.             END;
  234.             Reading := FALSE;
  235.             XoffSent := FALSE;
  236.         END;
  237.     END StopReading;
  238.  
  239. BEGIN
  240.     Reading := FALSE;
  241. END RS232Receiver;
  242.  
  243.  
  244.  
  245. PROCEDURE Init( portNumber      :CARDINAL;      (* 1 or 2       *)
  246.                 baudRate        :CARDINAL;      (* 300..38400   *)
  247.                 nStopBits       :CARDINAL;      (* 1 or 2       *)
  248.                 parityEnable    :BOOLEAN;
  249.                 evenParity      :BOOLEAN;
  250.                 charSize        :CARDINAL;      (* 5..8         *)
  251.                 inBufferSize    :CARDINAL;      (* input buffer size *)
  252.                 VAR done        :BOOLEAN        (* success      *)
  253.               );
  254. VAR lcr     :BITSET;
  255.     baud    :CARDINAL;
  256. BEGIN
  257.     StopReading;
  258.     IF BuffSize > 0 THEN DEALLOCATE( BuffPtr, BuffSize ) END;
  259.     IF portNumber = 1 THEN
  260.         ComBase := Com1Base;
  261.         ComIntNo := Com1IntNo;
  262.     ELSIF portNumber = 2 THEN
  263.         ComBase := Com2Base;
  264.         ComIntNo := Com2IntNo;
  265.     ELSE done := FALSE; RETURN
  266.     END;
  267.     ComPort := portNumber;
  268.     ComRX := ComBase + RX;
  269.     ComLSR := ComBase + LSR;
  270.     lcr := BITSET( charSize - 5 );
  271.     IF nStopBits > 1 THEN lcr := lcr + {2} END;
  272.     IF parityEnable THEN lcr := lcr + {3} END;
  273.     IF  evenParity THEN lcr := lcr + {4} END;
  274.     IF baudRate MOD 50 = 0 THEN
  275.         baud := 2304 DIV (baudRate DIV 50);
  276.     ELSIF baudRate = 75 THEN
  277.         baud := 1536
  278.     ELSIF baudRate = 110 THEN
  279.         baud := 1047
  280.     ELSE
  281.         done := FALSE;
  282.         RETURN
  283.     END;
  284.     BuffSize := inBufferSize;
  285.     InPtr    := 0;
  286.     OutPtr   := 0;
  287.     InCount  := 0;
  288.     IF Available( BuffSize ) THEN
  289.         ALLOCATE( BuffPtr, BuffSize )
  290.     ELSE
  291.         done := FALSE;
  292.         RETURN
  293.     END;
  294.     XoffThreshold := BuffSize DIV 4 * 3;
  295.     XonThreshold := BuffSize DIV 2;
  296.     ASM
  297.         MOV     BX, ComBase
  298.         LEA     DX, [BX+LCR]        (* DLAB := 1 *)
  299.         MOV     AL, 80H
  300.         OUT     DX, AL
  301.  
  302.         MOV     AX, baud
  303.         LEA     DX, [BX+DivL]       (* set divisor *)
  304.         OUT     DX, AL
  305.         JMP     slow                (* for PC/AT *)
  306.       slow:
  307.         INC     DX
  308.         MOV     AL, AH
  309.         OUT     DX, AL
  310.         JMP     slow2
  311.       slow2:
  312.  
  313.         LEA     DX, [BX+LCR]        (* line control *)
  314.         MOV     AL, lcr
  315.         OUT     DX, AL
  316.     END;
  317.     IF (portNumber = 1) & NOT ISR1active THEN
  318.         NewProcess( ComInt1, 512, TRUE );   (* create the ISR process *)
  319.         ISR1active := TRUE;
  320.     ELSIF NOT ISR2active THEN
  321.         NewProcess( ComInt2, 512, TRUE );
  322.         ISR1active := TRUE;
  323.     END;
  324.     done := TRUE;
  325. END Init;
  326.  
  327.  
  328. PROCEDURE ResetPars( baudRate        :CARDINAL;      (* 300..38400   *)
  329.                      nStopBits       :CARDINAL;      (* 1 or 2       *)
  330.                      parityEnable    :BOOLEAN;
  331.                      evenParity      :BOOLEAN;
  332.                      charSize        :CARDINAL;      (* 5..8         *)
  333.                      VAR done        :BOOLEAN        (* success      *)
  334.                      );
  335. VAR lcr     :BITSET;
  336.     baud    :CARDINAL;
  337. BEGIN
  338.     StopReading;
  339.     ComRX := ComBase + RX;
  340.     ComLSR := ComBase + LSR;
  341.     lcr := BITSET( charSize - 5 );
  342.     IF nStopBits > 1 THEN lcr := lcr + {2} END;
  343.     IF parityEnable THEN lcr := lcr + {3} END;
  344.     IF  evenParity THEN lcr := lcr + {4} END;
  345.     IF baudRate MOD 50 = 0 THEN
  346.         baud := 2304 DIV (baudRate DIV 50);
  347.     ELSIF baudRate = 75 THEN
  348.         baud := 1536
  349.     ELSIF baudRate = 110 THEN
  350.         baud := 1047
  351.     ELSE
  352.         done := FALSE;
  353.         RETURN
  354.     END;
  355.     ASM
  356.         MOV     BX, ComBase
  357.         LEA     DX, [BX+LCR]        (* DLAB := 1 *)
  358.         MOV     AL, 80H
  359.         OUT     DX, AL
  360.  
  361.         MOV     AX, baud
  362.         LEA     DX, [BX+DivL]       (* set divisor *)
  363.         OUT     DX, AL
  364.         JMP     slow                (* for PC/AT *)
  365.       slow:
  366.         INC     DX
  367.         MOV     AL, AH
  368.         OUT     DX, AL
  369.         JMP     slow2
  370.       slow2:
  371.  
  372.         LEA     DX, [BX+LCR]        (* line control *)
  373.         MOV     AL, lcr
  374.         OUT     DX, AL
  375.     END;
  376.     StartReading;
  377.     done := TRUE;
  378. END ResetPars;
  379.  
  380.  
  381. PROCEDURE GetCom( VAR ch :CHAR; VAR received :BOOLEAN );
  382. BEGIN
  383.     IF InCount > 0 THEN
  384.         ch := BuffPtr^[OutPtr];
  385.         received := TRUE;
  386.         INC( OutPtr );
  387.         IF OutPtr >= BuffSize THEN OutPtr := 0 END;
  388.         ASM (* guarantee single 8086 instruction *)
  389.             DEC     InCount
  390.         END;
  391.     ELSE
  392.         ch := 0C;
  393.         received := FALSE;
  394.     END;
  395.     IF XoffSent & (InCount < XonThreshold) THEN
  396.         PutCom( CtrlQ );
  397.         XoffSent := FALSE;
  398.     END;
  399. END GetCom;
  400.  
  401.  
  402. PROCEDURE PutCom( ch :CHAR );
  403. BEGIN
  404.     ASM
  405.         MOV     BX, ComBase
  406.         LEA     DX, [BX+LSR]
  407.       wait:   (* wait for tx buffer to empty *)
  408.         IN      AL, DX
  409.         TEST    AL, 20H
  410.         JZ      wait
  411.         MOV     AL, ch
  412.         LEA     DX, [BX+TX]
  413.         OUT     DX, AL
  414.     END;
  415. END PutCom;
  416.  
  417.  
  418. PROCEDURE XON;
  419. BEGIN
  420.     xon := TRUE;
  421. END XON;
  422.  
  423.  
  424. PROCEDURE XOFF;
  425. BEGIN
  426.     xon := FALSE;
  427.     IF XoffSent THEN PutCom( CtrlQ ); XoffSent := FALSE END;
  428. END XOFF;
  429.  
  430.  
  431. PROCEDURE Stop;
  432. BEGIN
  433.     StopReading;
  434.     ResetVector( Com1IntNo, OldCom1Int );
  435.     ResetVector( Com2IntNo, OldCom2Int );
  436. END Stop;
  437.  
  438.  
  439. VAR p   :POINTER TO CARDINAL;
  440.     ok  :BOOLEAN;
  441.  
  442. BEGIN
  443.     (* get COM port addresses from BIOS *)
  444.     p.SEG := 40H;                       (* BIOS DATA segment *)
  445.     p.OFS := 0; Com1Base := p^;
  446.     p.OFS := 2; Com2Base := p^;
  447.  
  448.     (* initialize general variables *)
  449.     XoffSent := FALSE;
  450.     BuffSize := 0;
  451.     xon := FALSE;
  452.  
  453.     ISR1active := FALSE;
  454.     ISR2active := FALSE;
  455.  
  456.     (* save current COM ISRs *)
  457.     GetVector( Com1IntNo, OldCom1Int );
  458.     GetVector( Com2IntNo, OldCom2Int );
  459.     TermProcedure( Stop );
  460.  
  461.     (* setup process stuff *)
  462.     InitSignal( RS232Input );           (* Inititalize our signal *)
  463.  
  464. END RS232.